using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;

namespace Pegasus.Windows.Forms.Explorer
{
	/// <summary>
	/// Collection of explorer nodes
	/// </summary>
	public class ExplorerNodeCollection : IList<ExplorerNode>, ICollection<ExplorerNode>, IEnumerable<ExplorerNode>
	{
		// Local Instance Value
		private bool m_readOnly = false;
		private List<ExplorerNode> m_nodeList = new List<ExplorerNode>();
		private Dictionary<string, ExplorerNode> m_nodeKeyTable = new Dictionary<string, ExplorerNode>();

		/// <summary>
		/// Initializes a new instance of the <see cref="ExplorerNodeCollection"/> class.
		/// </summary>
		public ExplorerNodeCollection()
		{
		}

		/// <summary>
		/// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <value></value>
		/// <returns>The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</returns>
		public int Count
		{
			get
			{
				return m_nodeList.Count;
			}
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
		/// </summary>
		/// <value></value>
		/// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.</returns>
		public bool IsReadOnly
		{
			get
			{
				return m_readOnly;
			}
		}

		/// <summary>
		/// Gets or sets the <see cref="Pegasus.Windows.Forms.Explorer.ExplorerNode"/> at the specified index.
		/// </summary>
		/// <value></value>
		public ExplorerNode this[ int index ]
		{
			get
			{
				return m_nodeList[ index ];
			}

			set
			{
				ThrowIfReadOnly();
				m_nodeList[ index ] = value;
			}
		}

		/// <summary>
		/// Gets or sets the <see cref="Pegasus.Windows.Forms.Explorer.ExplorerNode"/> with the specified key.
		/// </summary>
		/// <value></value>
		public ExplorerNode this[ string key ]
		{
			get
			{
				return m_nodeKeyTable[ key ];
			}

			set
			{
				ThrowIfReadOnly();
				m_nodeKeyTable[ key ] = value;
			}
		}

		/// <summary>
		/// Adds the specified item.
		/// </summary>
		/// <param name="node">The node.</param>
		public void Add( ExplorerNode node )
		{
			m_nodeList.Add( node );
			AddKeyTable( node );

			FireOnNodeAdded( node );
		}

		/// <summary>
		/// Adds the specified text.
		/// </summary>
		/// <param name="text">The text.</param>
		public void Add( string text )
		{
			ExplorerNode node = new ExplorerNode();
			node.Text = text;

			Add( node );
		}

		/// <summary>
		/// Adds the specified key.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <param name="text">The text.</param>
		public void Add( string key, string text )
		{
			ExplorerNode node = new ExplorerNode();
			node.Key = key;
			node.Text = text;

			Add( node );
		}

		/// <summary>
		/// Inserts an item to the <see cref="T:System.Collections.Generic.IList`1"/> at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
		/// <param name="node">The object to insert into the <see cref="T:System.Collections.Generic.IList`1"/>.</param>
		public void Insert( int index, ExplorerNode node )
		{
			m_nodeList.Insert( index, node );
			AddKeyTable( node );

			FireOnNodeAdded( node );
		}

		/// <summary>
		/// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"/> contains a specific value.
		/// </summary>
		/// <param name="node">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
		/// <returns>
		/// true if <paramref name="node"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false.
		/// </returns>
		public bool Contains( ExplorerNode node )
		{
			return m_nodeList.Contains( node );
		}

		/// <summary>
		/// Determines whether [contains] [the specified key].
		/// </summary>
		/// <param name="key">The key.</param>
		/// <returns>
		/// 	<c>true</c> if [contains] [the specified key]; otherwise, <c>false</c>.
		/// </returns>
		public bool ContainsKey( string key )
		{
			return m_nodeKeyTable.ContainsKey( key );
		}

		/// <summary>
		/// Indexes the of.
		/// </summary>
		/// <param name="node">The node.</param>
		/// <returns></returns>
		public int IndexOf( ExplorerNode node )
		{
			return m_nodeList.IndexOf( node );
		}

		/// <summary>
		/// Indexes the of.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <returns></returns>
		public int IndexOf( string key )
		{
			if( m_nodeKeyTable.ContainsKey( key ) )
			{
				return IndexOf( m_nodeKeyTable[ key ] );
			}

			return -1;
		}

		/// <summary>
		/// Copies all the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>.
		/// </summary>
		/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param>
		public void CopyTo( ExplorerNode[] array )
		{
			m_nodeList.CopyTo( array );
		}

		/// <summary>
		/// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
		/// </summary>
		/// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param>
		/// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param>
		public void CopyTo( ExplorerNode[] array, int arrayIndex )
		{
			m_nodeList.CopyTo( array, arrayIndex );
		}

		/// <summary>
		/// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		public void Clear()
		{
			FireOnCollectionClearing();

			m_nodeList.Clear();
			m_nodeKeyTable.Clear();

			FireOnCollectionCleared();
		}

		/// <summary>
		/// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </summary>
		/// <param name="node">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
		/// <returns>
		/// true if <paramref name="node"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
		/// </returns>
		public bool Remove( ExplorerNode node )
		{
			RemoveKeyTable( node );
			bool ret = m_nodeList.Remove( node );
			if( ret )
			{
				FireOnNodeRemoved( node );
			}

			return ret;
		}

		/// <summary>
		/// Removes the specified key.
		/// </summary>
		/// <param name="key">The key.</param>
		/// <returns></returns>
		public bool Remove( string key )
		{
			if( m_nodeKeyTable.ContainsKey( key ) )
			{
				return Remove( m_nodeKeyTable[ key ] );
			}

			return false;
		}

		/// <summary>
		/// Removes the <see cref="T:System.Collections.Generic.IList`1"/> item at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index of the item to remove.</param>
		public void RemoveAt( int index )
		{
			ExplorerNode node = m_nodeList[ index ];
			m_nodeList.RemoveAt( index );

			if( node != null )
			{
				RemoveKeyTable( node );
				FireOnNodeRemoved( node );
			}
		}

		/// <summary>
		/// Gets the enumerator.
		/// </summary>
		/// <returns></returns>
		public IEnumerator<ExplorerNode> GetEnumerator()
		{
			return m_nodeList.GetEnumerator();
		}

		/// <summary>
		/// Gets the enumerator.
		/// </summary>
		/// <returns></returns>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return m_nodeList.GetEnumerator();
		}

		/// <summary>
		/// Adds to key table.
		/// </summary>
		/// <param name="node">The node.</param>
		private void AddKeyTable( ExplorerNode node )
		{
			if( !string.IsNullOrEmpty( node.Key ) )
			{
				m_nodeKeyTable.Add( node.Key, node );
			}
		}

		/// <summary>
		/// Throws if read only.
		/// </summary>
		private void ThrowIfReadOnly()
		{
			if( m_readOnly )
			{
				throw new ReadOnlyException( "The collection is read-only" );
			}
		}

		/// <summary>
		/// Removes the key table.
		/// </summary>
		/// <param name="node">The node.</param>
		private void RemoveKeyTable( ExplorerNode node )
		{
			if( !string.IsNullOrEmpty( node.Key ) )
			{
				m_nodeKeyTable.Remove( node.Key );
			}
		}

		/// <summary>
		/// Fires the on collection cleared event.
		/// </summary>
		private void FireOnCollectionClearing()
		{
			if( OnCollectionClearing != null )
			{
				OnCollectionClearing( this, EventArgs.Empty );
			}
		}

		/// <summary>
		/// Fires the on collection cleared event.
		/// </summary>
		private void FireOnCollectionCleared()
		{
			if( OnCollectionCleared != null )
			{
				OnCollectionCleared( this, EventArgs.Empty );
			}
		}

		/// <summary>
		/// Fires the on node added event.
		/// </summary>
		/// <param name="node">The node.</param>
		private void FireOnNodeAdded( ExplorerNode node )
		{
			if( OnNodeAdded != null )
			{
				OnNodeAdded( this, new ExplorerEventArgs( node ) );
			}
		}

		/// <summary>
		/// Fires the on node removed event.
		/// </summary>
		/// <param name="node">The node.</param>
		private void FireOnNodeRemoved( ExplorerNode node )
		{
			if( OnNodeRemoved != null )
			{
				OnNodeRemoved( this, new ExplorerEventArgs( node ) );
			}
		}

		/// <summary>
		/// Fired just before when the collection is cleared.
		/// </summary>
		public event EventHandler<EventArgs> OnCollectionClearing;

		/// <summary>
		/// Fired when the collection is cleared.
		/// </summary>
		public event EventHandler<EventArgs> OnCollectionCleared;

		/// <summary>
		/// Fired when a node is added to the collection.
		/// </summary>
		public event EventHandler<ExplorerEventArgs> OnNodeAdded;

		/// <summary>
		/// Fired when a node is removed from the collection.
		/// </summary>
		public event EventHandler<ExplorerEventArgs> OnNodeRemoved;

	}
}
